﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RimeAppDemo.DataProtocal;
using Rimelink.Data.LoraServer;
using RimeLoRaTool.LoraServer;
using ServiceTable.Tool;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace RimeAppDemo
{
    [ComVisible(true)]
    public class ScriptForWeb
    {
        private static readonly bool DEFAULT_CLASS_IS_C = "C".Equals(ConfigurationManager.AppSettings["defaultclass"].ToUpper());
        private static readonly bool DEFAULT_JOIN_IS_OTAA = "OTAA".Equals(ConfigurationManager.AppSettings["defaultjoin"].ToUpper());

        LoraServerContext _Context = new LoraServerContext(); // 用于登录
        LoraServerContext context = new LoraServerContext();
        ILoraServerIOPiper _Piper = null; // 用于登录
        ILoraServerIOPiper piper = null;

        Rimelink.Data.LoraServer.Impl.LoraServerService Service = null;

        public ScriptForWeb()
        {
            _Piper = new LoraServerIOPiper(_Context);
            piper = new LoraServerIOPiper(context); 
        }

        public string Hello()
        {
            return "Hello World!";
        }

        public string Login(string host, string username, string password)
        {
            _Context.Host = host; // 用于登录的 host 是支持直接带 :端口的

            int port = 8080;
            var serviceHost = host;
            if (host.Contains(":"))
            {
                try
                {
                    var items = host.Split(':');
                    serviceHost = items[0];
                    port = int.Parse(items[1]); 
                }
                catch (Exception e)
                {
                    return VR2Json(new ValidateResult(false, e.Message));
                }
            }
            var vr = _Piper.Login(username, password);
            // Form1.Instance.Log(_Context.ApiBaseUrl);
            Form1.Instance.Log(vr.Message);
            if (vr.IsValid)
            {
                context.Host = host;
                context.Token = vr.Message; 

                Service = new Rimelink.Data.LoraServer.Impl.LoraServerService(serviceHost, false, port, vr.Message);
            }
            vr.Message = vr.IsValid ? "登录成功" : "登录失败";
            Form1.Instance.State(vr.Message);
            return VR2Json(vr);
        }

        public string InitBaseCfg()
        {
            try
            {
                return InitBaseCfgAct();
            }catch(Exception e)
            {
                return VR2Json(new ValidateResult(false, e.Message));
            }
        }

        private string InitBaseCfgAct()
        {
            StringBuilder sb = new StringBuilder(1024);
            if (String.IsNullOrEmpty(context.Token))
            {
                Form1.Instance.Log("未正确登录！");
                sb.Append("<div class='item_err'>未正确登录！</div>");
                return VR2Json(new ValidateResult(false, sb.ToString())); 
            }
            var olist = piper.GetOrganizationList(1, 0); 
            if (olist.Count == 0)
            {
                Form1.Instance.Log("未能发现初始化机构，as 似乎未正确安装！");
                sb.Append("<div class='item_err'>未能发现初始化机构！</div>");
                return VR2Json(new ValidateResult(false, sb.ToString()));
            }
            sb.Append("<div class='item'><span class='r'>√</span>Organization: " + olist[0].displayName + "</div>");
            Form1.Instance.Log("Organization : " + olist[0].displayName);
            context.OrganizationID = int.Parse(olist[0].id);

            ValidateResult vr = null;
            var nlist = piper.GetNetworkServerList(1, 0); 
            if (nlist.Count == 0)
            {
                vr = piper.AddNetworkServer("rimelink_local_ns", "localhost:8000");
                Form1.Instance.Log("添加 network server rimelink_local_ns : " + vr.Message);
                if (!vr.IsValid)
                {
                    // 可能是 docker 情开再尝试一下 "chirpstack-network-server:8000"
                    vr = piper.AddNetworkServer("rimelink_docker_ns", "chirpstack-network-server:8000");
                    Form1.Instance.Log("添加 network server rimelink_local_ns : " + vr.Message);
                    if (!vr.IsValid)
                    {
                        sb.Append("<div class='item'><span class='w'>×</span>network server</div>");
                        sb.Append("<div class='item_err'>未能正确添加 network server！</div>");
                        return VR2Json(new ValidateResult(false, sb.ToString()));
                    }
                }
                nlist = piper.GetNetworkServerList(1, 0);
            }
            context.NetworkServerID = int.Parse(nlist[0].id);
            Form1.Instance.Log("netword server : " + nlist[0].name + " " + nlist[0].server);
            sb.Append("<div class='item'><span class='r'>√</span>network server : " + nlist[0].name + "</div>");

            var slist = piper.GetServiceProfileList(1, 0, context.OrganizationID); 
            if (slist.Count == 0)
            {
                vr = piper.AddServiceProfile("rimelink_local_service_profile", context.NetworkServerID, true, context.OrganizationID);
                Form1.Instance.Log("添加 service profile rimelink_local_service_profile : " + vr.Message);
                if (!vr.IsValid)
                {
                    sb.Append("<div class='item'><span class='w'>×</span>service profile</div>");
                    sb.Append("<div class='item_err'>未能正确添加 service profile！</div>");
                    return VR2Json(new ValidateResult(false, sb.ToString()));
                }
                slist = piper.GetServiceProfileList(1, 0, context.OrganizationID);
            }
            context.ServiceProfileID = slist[0].id;
            Form1.Instance.Log("service profile : " + slist[0].id + " " + slist[0].name);
            sb.Append("<div class='item'><span class='r'>√</span>service profile</div>");

            var glist = piper.GetGatewayProfileList(1, 0, context.NetworkServerID); 
            if (glist.Count == 0)
            {
                vr = piper.AddGatewayProfile("rimelink_gateway", context.NetworkServerID, "80,81,82,83,84,85,86,87");
                Form1.Instance.Log("添加 gateway profile rimelink_gateway : " + vr.Message);
                if (!vr.IsValid)
                {
                    sb.Append("<div class='item'><span class='w'>×</span>gateway profile</div>");
                    sb.Append("<div class='item_err'>未能正确添加 gateway profile！</div>");
                    return VR2Json(new ValidateResult(false, sb.ToString()));
                }
                glist = piper.GetGatewayProfileList(1, 0, context.NetworkServerID);
            }
            context.GatewayProfileID = glist[0].id;
            Form1.Instance.Log("gateway profile : " + glist[0].id + " " + glist[0].name);
            sb.Append("<div class='item'><span class='r'>√</span>gateway profile</div>");

            var dlist = piper.GetDeviceProfileList(20, 0, context.OrganizationID); 
            foreach (var dp in dlist)
            {
                DeviceProfile item = null;
                try
                {
                    item = piper.GetDeviceProfile(dp.id);
                }
                catch (Exception de)
                {
                    Form1.Instance.Log("device profile " + dp.id + " : " + de.Message);
                    continue;
                }
                if (item.supportsJoin)
                {
                    if (item.supportsClassC)
                    {
                        context.DeviceProfileOTAA_C = item.id;
                    }
                    else
                    {
                        context.DeviceProfileOTAA = item.id;
                    }
                }
                else
                {
                    if (item.supportsClassC)
                    {
                        context.DeviceProfileABP_C = item.id;
                    }
                    else
                    {
                        context.DeviceProfileABP = item.id;
                        sb.Append("<div class='item'><span class='r'>√</span>device profile</div>");
                    }
                }
                Form1.Instance.Log("device profile : " + dp.id + " " + dp.name + String.Format("regParamsRevision:{0}，macVersion:{1}, join:{2}, class c:{3}", item.regParamsRevision, item.macVersion, item.supportsJoin, item.supportsClassC));
            }
            if (String.IsNullOrEmpty(context.DeviceProfileABP))
            {
                vr = piper.AddDeviceProfile("rimelink_demo", context.NetworkServerID, false, false, context.OrganizationID);
                Form1.Instance.Log("添加 device profile rimelink_demo : " + vr.Message);
                if (!vr.IsValid)
                {
                    sb.Append("<div class='item'><span class='w'>×</span>device profile</div>");
                    sb.Append("<div class='item_err'>未能正确添加 device profile！</div>");
                    return VR2Json(new ValidateResult(false, sb.ToString()));
                }
                context.DeviceProfileABP = vr.Message;
                sb.Append("<div class='item'><span class='r'>√</span>device profile</div>");
            }
            if (String.IsNullOrEmpty(context.DeviceProfileABP_C))
            {
                vr = piper.AddDeviceProfile("deviceprofileABP_C", context.NetworkServerID, false, true, context.OrganizationID);
                if (vr.IsValid)
                {
                    context.DeviceProfileABP_C = vr.Message;
                }                
            }
            if (String.IsNullOrEmpty(context.DeviceProfileOTAA))
            {
                vr = piper.AddDeviceProfile("deviceprofileOTAA", context.NetworkServerID, true, false, context.OrganizationID);
                if (!vr.IsValid)
                {
                    context.DeviceProfileOTAA = vr.Message;
                }
            }
            if (String.IsNullOrEmpty(context.DeviceProfileOTAA_C))
            {
                vr = piper.AddDeviceProfile("deviceprofileOTAA_C", context.NetworkServerID, true, true, context.OrganizationID);
                if (!vr.IsValid)
                {
                    context.DeviceProfileOTAA_C = vr.Message;
                }                
            }
            // 增加应用
            var alist = piper.GetApplicationList(1, 0, context.OrganizationID); 
            if (alist.Count == 0)
            {
                var app = piper.AddApplication("rimelink_application_01", context.OrganizationID, context.ServiceProfileID, "uplink temperature and humidity, downlink to switch LED");
                Form1.Instance.Log("添加 application rimelink_application_01 : " + app.Message);
                if (!vr.IsValid)
                {
                    sb.Append("<div class='item'><span class='w'>×</span>application</div>");
                    sb.Append("<div class='item_err'>未能正确添加 application！</div>");
                    return VR2Json(new ValidateResult(false, sb.ToString()));
                }
                alist = piper.GetApplicationList(1, 0, context.OrganizationID);
            }
            context.ApplicationID = int.Parse(alist[0].id);
            Form1.Instance.Log("application : " + alist[0].id + " " + alist[0].name);
            sb.Append("<div class='item'><span class='r'>√</span>application : <b id='appnamelabel'>" + alist[0].name + "</b>&nbsp;<span id='appmore'><span onclick='loadmoreapp()' style='color:blue;cursor:pointer;'>更多</span></span></div>");

            context.IsReady = true;
            var jresult = VR2JObject(new ValidateResult(true, sb.ToString()));
            jresult["gateway_cnt"] = piper.GetGatewayCnt(context.OrganizationID);
            jresult["device_cnt"] = piper.GetDeviceCnt(context.ApplicationID);
            return jresult.ToString();
        }

        public string AddGateway(string mac)
        {
            var vr = piper.AddGateway(mac, mac, mac, context.GatewayProfileID, context.NetworkServerID, context.OrganizationID);
            Form1.Instance.Log("添加网关 " + mac + ":" + vr.Message);
            if (vr.IsValid)
            {
                var cnt = piper.GetGatewayCnt(context.OrganizationID);
                var vo = VR2JObject(vr);
                vo["gateway_cnt"] = cnt;
                return vo.ToString();
            }
            return VR2Json(vr);
        }

        public string AddDevices(string devices)
        {
            if (String.IsNullOrEmpty(devices))
            {
                return VR2Json(new ValidateResult(false, "请按正确的格式添加设备。")); 
            }
            var euiList = Regex.Replace(devices.ToUpper(), "[^0-9A-F]+", ",");
            var devEUIList = euiList.Split(',').ToList();
            int cnt = 0;
            foreach (var eui in devEUIList)
            {
                if (String.IsNullOrEmpty(eui) || eui.Length != 16) continue;
                var vr = piper.AddDevice(context.ApplicationID, eui, eui, (DEFAULT_CLASS_IS_C ? 
                    (DEFAULT_JOIN_IS_OTAA ? context.DeviceProfileOTAA_C : context.DeviceProfileABP_C) : 
                    (DEFAULT_JOIN_IS_OTAA ? context.DeviceProfileOTAA : context.DeviceProfileABP)), 
                    eui);
                Form1.Instance.Log("添加设备 " + eui + " : " + (vr.IsValid ? "添加成功" : vr.Message));
                if (vr.IsValid)
                {
                    cnt++;
                    if (!DEFAULT_JOIN_IS_OTAA)   
                    {
                        var dr = piper.UpdateDeviceActivation(eui, eui.Substring(8), LoraServerContext.DEFAULT_APP_ENC_KEY, LoraServerContext.DEFAULT_APP_ENC_KEY);
                    }
                    else
                    {
                        var dr = piper.AddDeviceKey(eui, LoraServerContext.DEFAULT_APP_ENC_KEY);
                    }
                }
            }

            var result = new ValidateResult(true, "成功添加 " + cnt + " 个");
            if (cnt > 0)
            {
                var dcnt = piper.GetDeviceCnt(context.ApplicationID);
                var vo = VR2JObject(result);
                vo["device_cnt"] = dcnt;
                return vo.ToString();
            }
            return VR2Json(result);
        }

        #region 组播相关 

        public string MakeMulticastGroup(string macAddr, string devEUI, string etype, string evalue)
        {
            if (Service == null) return VR2Json(new ValidateResult(false, "尚未登录"));

            if (String.IsNullOrEmpty(macAddr) || !Regex.IsMatch(macAddr, "^[0-9A-Fa-f]{8}$")) return VR2Json(new ValidateResult(false, "组播地址不正确！"));

            if (!String.IsNullOrEmpty(devEUI) && !Regex.IsMatch(devEUI, "^[0-9A-Fa-f]{16}$")) return VR2Json(new ValidateResult(false, "devEUI不正确！"));

            if ("2".Equals(etype))
            {
                if (String.IsNullOrEmpty(evalue) || !Regex.IsMatch(evalue, "^[0-9A-Fa-f]{32}$")) return VR2Json(new ValidateResult(false, "mcKey_encrypted不正确！"));
            }

            var mg = Service.GetMulticastGroupInfoByName(macAddr);
            if (mg == null)
            {
                var amgvr = new Rimelink.Data.LoraServer.Model.ValidateResult(false, "未初始化");
                if ("0".Equals(etype))
                {
                    amgvr = Service.AddMulticastGroup(macAddr, macAddr, context.ServiceProfileID);
                }
                else if ("1".Equals(etype))
                {
                    Rimelink.Data.Helper.Keys keys = Rimelink.Data.Helper.MulticastHelper.GetKeys(macAddr, LoraServerConstant.DEFAULT_APP_ENC_KEY, LoraServerConstant.DEFAULT_APP_ENC_KEY);
                    amgvr = Service.AddMulticastGroup(macAddr, macAddr, context.ServiceProfileID, keys.McAppSKey, keys.McNetSKey);
                }
                else if ("2".Equals(etype))
                {
                    Rimelink.Data.Helper.Keys keys = Rimelink.Data.Helper.MulticastHelper.GetKeys(macAddr, LoraServerConstant.DEFAULT_APP_ENC_KEY, evalue);
                    amgvr = Service.AddMulticastGroup(macAddr, macAddr, context.ServiceProfileID, keys.McAppSKey, keys.McNetSKey);
                }

                if (!amgvr.IsValidate)
                {
                    Form1.Instance.Log("添加组播地址失败： " + macAddr + ":" + amgvr.Message);
                    return VR2Json(new ValidateResult(false, amgvr.Message));
                }
                mg = Service.GetMulticastGroupInfo(amgvr.Message);  // 这里理论上不会出错。
            }

            Form1.Instance.Log("组播信息： " + macAddr + ":" + mg.id);
            if (!String.IsNullOrEmpty(devEUI))
            {
                var amgd = Service.AddMulticastGroupDevice(mg.id, devEUI);
                Form1.Instance.Log("添加组播节点： " + devEUI + ":" + amgd.Message);
            }

            var vo = VR2JObject(new ValidateResult(true, "获取成功！"));
            vo["id"] = mg.id;
            vo["name"] = mg.name;
            vo["addr"] = mg.mcAddr;
            return vo.ToString();
        }

        public string Multicast(string mgid, string data)
        {
            if (Service == null) return VR2Json(new ValidateResult(false, "尚未登录"));

            if(String.IsNullOrEmpty(data) || String.IsNullOrEmpty(mgid)) return VR2Json(new ValidateResult(false, "请输入消息内容！"));

            string base64 = null;
            if (data.StartsWith("0x"))
            {
                try
                {
                    base64 = Convert.ToBase64String(ByteHelper.HexStringToBytes(data.Substring(2)));
                }
                catch(Exception be)
                {
                    return VR2Json(new ValidateResult(false, be.Message));
                }
            }
            else
            {
                base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(data));
            }

            var vr = Service.Multicast(mgid, base64);
            if (vr.IsValidate)
            {
                Form1.Instance.Log("发送组播消息： " + data + ": fCnt = " + vr.Message);
                return VR2Json(new ValidateResult(true, "发送成功：fCnt = " + vr.Message));
            }

            Form1.Instance.Log("发送组播消息： " + data + ":" + vr.Message);
            return VR2Json(new ValidateResult(false, vr.Message));
        }

        #endregion

        #region 获取应用，设置当前应用

        public string ApplicationList(int limit)
        {
            JArray list = new JArray();
            try
            {
                var alist = piper.GetApplicationList(limit, 0, context.OrganizationID);
                foreach (var app in alist)
                {
                    JObject japp = new JObject();
                    japp["id"] = app.id;
                    japp["name"] = app.name;
                    list.Add(japp);
                }
            }
            catch (Exception ae)
            {
                Form1.Instance.Log(ae.Message);
            }
            return list.ToString(Formatting.None);
        }

        public void SetContextApplicationId(int id)
        {
            context.ApplicationID = id;
            Form1.Instance.Log(String.Format("new context.ApplicationId={0}", id));
        }

        #endregion

        public void Navigate(string url)
        {
            if (url.StartsWith("@"))
            {
                url = Application.StartupPath + @"/" + Regex.Replace(url, "^@", "");
            }
            Form1.Instance.Navigate(url);
        }

        private string VR2Json(ValidateResult vr)
        { 
            return VR2JObject(vr).ToString();
        }

        private JObject VR2JObject(ValidateResult vr)
        {
            JObject j = new JObject();
            j["err_code"] = vr.IsValid ? 200 : 101;
            j["err_msg"] = vr.Message;
            return j;
        }
    }
}
